/*
Copyright (C) 2013 Jesper Berent Nygaard Pedersen

Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <termios.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/serial.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "stokermon.h"

#define TIMER1_SIGNAL     (SIGRTMAX)
#define TTY_BUF_SIZE 1024

struct sigaction saved_sigio_handler;
int fd;
char buf[TTY_BUF_SIZE];
static int pos = 0;
struct t_dataset dataset;


/*
 =======================================================================================================================
 =======================================================================================================================
 */

void send_msg(char *s, int len)
{
	int i;
	char end_marker[2] = { 0x0d, 0x0a };
	unsigned char cs = 0;
	for (i=0; i<len; i++)
		cs ^= s[i];
	write(fd, s, len);
	write(fd, end_marker, 2);
//	write(fd, &cs, 1); // checksum not used for old interface
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void tty_signal_handler(int status)
{
  int   i, res;
  struct tm *time;
  int parameters;
  char fname[256];
  res = read(fd, &buf[pos], sizeof(buf)-pos);  /* Could use IOCTL instead */
  pos += res;
  if (pos>=sizeof(buf))
    pos = 0;

  // The strings appear to be terminated by CR/LF
  // The old scotte chip version (prior to 4.99) does not use checksum i.e. checksum control is not handled !
  for( i = 0; i < pos; i++)
    if (buf[i] == 0x0a || buf[i] == 0x0d)
    {
        buf[i] = 0x00;
        pos = 0;
        if (strlen(buf) >= 72) // Assume string is valid at this length
        {
            gettimeofday(&dataset.time, NULL);
            time = localtime(&dataset.time.tv_sec);
			if (time->tm_year+1900 > 2012) // Ignore data if time is not set
			{
	//				printf("\nRX[%i]=(%s)\n",strlen(buf),buf); // Echo to terminal for debugging
				parameters = sscanf(buf," %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
							&dataset.drift_effekt,&dataset.setup_effekt,
							&dataset.temp_kedel,&dataset.temp_skakt,
							&dataset.temp_roeg,&dataset.ilt,
							&dataset.lys,&dataset.snegl_sek,
							&dataset.el_sek,&dataset.alarm,
							&dataset.ref_kedel,&dataset.ref_ilt,
							&dataset.kapacitet,&dataset.skorsten,
							&dataset.maetning_lav,&dataset.maetning_hoj,
							&dataset.tilstand,&dataset.styringstype);

	/*				printf(    " %04u-%02-%02x %02u;%02u.%02u\n"
						   " data          %i\n"
						   " drift_effekt  %u %%\n setup_effekt  %.01f kW\n temp_kedel    %.01f C\n temp_skakt    %u C\n"
						   " temp_roeg     %u C\n ilt           %.01f %%\n lys           %u %%\n snegl_sek     %u s\n"
						   " el_sek        %u s\n alarm         %u\n ref_kedel     %u C\n ref_ilt       %.01f %%\n"
						   " kapacitet     %u g/6min\n skorsten      %u\n maetning_lav  %.02f %%\n maetning_hoj  %.01f %%\n"
						   " tilstand      %u\n styringstype  %u\n\n",
							time->tm_year+1900, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec,
							dataset.drift_effekt,(float)dataset.setup_effekt / 10.0,
							(float) dataset.temp_kedel / 10.0,dataset.temp_skakt,
							dataset.temp_roeg,(float) dataset.ilt /10.0,
							dataset.lys,dataset.snegl_sek,
							dataset.el_sek,dataset.alarm,
							dataset.ref_kedel,(float)dataset.ref_ilt / 10.0,
							dataset.kapacitet,dataset.skorsten,
							(float) dataset.maetning_lav / 100.0,(float) dataset.maetning_hoj / 10.0,
							dataset.tilstand,dataset.styringstype);
	*/
				if (parameters == 18) // All parameters correctly identified
				{
					int f;
					// Append the data to our 'database' files
					sprintf(fname, "%s/%04u_%02u_%02u.data", DATA_PATH, time->tm_year+1900, time->tm_mon+1, time->tm_mday);
					f = open(fname, O_WRONLY | O_APPEND | O_CREAT, 0666);
					write(f, &dataset, sizeof(struct t_dataset));
					close(f);
				}
			}
        }
    }
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
int tty_init()
{
  int               error;
  struct termios    newtio;
  struct sigaction  saio; /* definition of signal action */

  fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NONBLOCK );
  if(fd < 0)
  {
    return -1;
  }

  /* install the serial handler before making the device asynchronous */
  saio.sa_handler = tty_signal_handler;
  sigemptyset(&saio.sa_mask);
  saio.sa_flags = 0;
  saio.sa_restorer = NULL;
  sigaction(SIGIO, &saio, &saved_sigio_handler);
  /* allow the process to receive SIGIO */
  fcntl(fd, F_SETOWN, getpid());
  fcntl(fd, F_SETFL, FASYNC);            /* Asynchroneous */
  newtio.c_cflag = B9600 | CS8 | CLOCAL | CREAD;
  newtio.c_iflag = IGNPAR | IGNBRK  ;
  newtio.c_oflag = 0;
  newtio.c_lflag = 0;
  newtio.c_cc[VMIN] = 0;
  newtio.c_cc[VTIME] = 0;
  tcflush(fd, TCIFLUSH);
  tcsetattr(fd, TCSANOW, &newtio);
  return 0;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
int tty_close(void)
{
  close(fd); /* close the com port */
  /* restore original signal handler */
  sigaction(SIGIO, &saved_sigio_handler, NULL);
  return 0;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void timerhandler(int signo, siginfo_t * info, void *context)
{
	send_msg("Z000000",7);
};

/*
 =======================================================================================================================
 =======================================================================================================================
 */
timer_t start_timer(int first, int interval)
{
  struct sigaction sigact;
  struct sigevent sigev;
  timer_t timerid;
  struct itimerspec itval;
  struct itimerspec oitval;

  sigemptyset(&sigact.sa_mask);
  sigact.sa_flags = SA_SIGINFO;
  sigact.sa_sigaction = timerhandler;

  // Create the POSIX timer to generate TIMER1_SIGNAL
  sigev.sigev_notify = SIGEV_SIGNAL;
  sigev.sigev_signo = TIMER1_SIGNAL;
  sigev.sigev_value.sival_ptr = &timerid;

   // Set up sigaction to catch signal
  if (sigaction(TIMER1_SIGNAL, &sigact, NULL) == -1) {
    perror("sigaction failed");
    return NULL;
	}

  if (timer_create(CLOCK_MONOTONIC, &sigev, &timerid) == 0) {
    itval.it_value.tv_sec = first / 1000;
    itval.it_value.tv_nsec = (long)(first % 1000) * (1000000L);;
	itval.it_interval.tv_sec = interval / 1000;
	itval.it_interval.tv_nsec = (long)(interval % 1000) * (1000000L);

    if (timer_settime(timerid, 0, &itval, &oitval) != 0) {
      perror("time_settime error!");
    }
  } else {
    perror("timer_create error!");
    return (timer_t)-1;
  }
  return timerid;
}

/*
 =======================================================================================================================
 =======================================================================================================================
 */
void main()
{
    struct timeval now;
    uint32_t first_timer;

	tty_init();

    // Start a interval timer which fires every 1 minute. Let's align it approximately to 00 seconds
    // so we need to calculate how long to wait before we fire it first time:
    gettimeofday(&now, NULL);
	first_timer = 1000*(60-(now.tv_sec%60));
	if (first_timer == 0)
        first_timer = 60;
    first_timer = first_timer - now.tv_usec / 1000;
	start_timer(first_timer, 60000); // 60s interval
	while (1)
	{
		sleep(1); // Everything happens in signal handlers from now on...
	}
	tty_close();
}
